﻿using System;
using System.Globalization;
using MathLib.Exp;

namespace MathLib
{
    public class Calculation
    {
        public static string Calculate(string str)
        {

            var exp = Split(str);

            return Calculate(exp).ToString(CultureInfo.InvariantCulture);
        }

        public static MethodExpression Split(string str)
        {

            if (string.IsNullOrEmpty(str))
            {
                return null;
            }
            var s = str.Replace(" ", string.Empty);

            var index = s.LastIndexOf('(');
            if (index > -1)
            {
                var end = s.IndexOf(')', index) + 1;
                var combineStr = s.Substring(index, end - index);
                var result = SplitCombine(combineStr);
                var temp = s.Replace(combineStr, result);
                Console.WriteLine(temp);
                return Split(temp);
            }
            
            index = s.IndexOfAny("*/".ToCharArray());
            //var method = GetMethod(s[index]);
            if (index > -1)
            {
                var indexL = 0;
                for (var i = index - 1; i > -1; i--)
                {
                    var c = s[i];
                    if (!char.IsNumber(c) && c != '.')
                    {
                        indexL = i;
                        break;
                    }
                }
                var l = indexL == 0 ? s.Substring(indexL , index - indexL ): s.Substring(indexL+1 , index - indexL-1 );

                var indexR = index;
                for (var i = index +1; i < s.Length; i++)
                {
                    var c = s[i];
                    if (char.IsNumber(c) || c == '.')
                    {
                        indexR = i;
                    }
                    else
                    {
                        break;
                    }
                }
                var r = s.Substring(index +1, indexR - index );
                var result = Calculate(new BinaryExpression
                    {
                        Method = GetMethod(s[index]),
                        Origin = s,
                        Left = Split(l),
                        Right = Split(r)
                    }).ToString();
                var temp = indexL == 0 ? s.Substring(indexL , indexR - indexL + 1): s.Substring(indexL +1, indexR - indexL);
                s = s.Replace(temp, result);
                Console.WriteLine(s);
                return Split(s);
            }
            index = s.IndexOfAny("+-".ToCharArray());
            if (index > -1)
            {
                var left = s.Substring(0, index);
                var right = s.Substring(index + 1, s.Length - index - 1);
                return new BinaryExpression { Method = GetMethod(s[index]), Origin = s, Left = Split(left), Right = Split(right) };
            }
            return new ValueExpression { Value = s };

        }

        private static string SplitCombine(string str)
        {
            var exp = Split(str.TrimStart('(').TrimEnd(')'));

            return Calculate(exp).ToString(CultureInfo.InvariantCulture);
        }

        private static decimal Calculate(MethodExpression exp)
        {
            if (exp.Type == ExpressionType.Value)
            {
                var s = (exp as ValueExpression).Value;
                return decimal.Parse(s);
            }
            if (exp.Type == ExpressionType.Binary)
            {
                var binary = exp as BinaryExpression;
                var math = MathFactory.Create(binary.Method);
                var l = Calculate(binary.Left);
                var r = Calculate(binary.Right);
                var result = math.Calculate(l, r);
                //Console.WriteLine("{0} = {1}", binary.Origin, result);
                return result;
            }
            if (exp.Type == ExpressionType.Combination)
            {
                var comb = exp as CombineExpression;
                var expression = Split(comb.Origin.Substring(1, comb.Origin.Length - 2));
                return Calculate(expression);
            }
            return 0;
        }

        private static MathMethod GetMethod(char c)
        {
            switch (c)
            {
                case '+':
                    return MathMethod.Add;
                case '-':
                    return MathMethod.Subtract;
                case '*':
                    return MathMethod.Multiply;
                case '/':
                    return MathMethod.Divide;
                case '(':
                case ')':
                    return MathMethod.Compounded;
            }
            throw new Exception("无法判断的符号" + c);
        }


    }
}
